﻿using B.S.BaseData.Domain.Entities;
using B.S.BaseData.Domain.Entities.RBAC;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace B.S.BaseData.WriteAPI.Audit
{
    /// <summary>
    /// 审计拦截器。
    /// 作用：在执行 SaveChanges 或 SaveChangesAsync 时，自动为所有继承自 BaseEntity 的实体设置审计字段（如创建人、修改人、删除人及时间等）。
    /// 适用于软删除、自动记录操作人等场景。
    /// </summary>
    public class AuditInterceptor : SaveChangesInterceptor
    {
        /// <summary>
        /// 用于获取当前 HttpContext，进而获取当前登录用户信息。
        /// </summary>
        private readonly IHttpContextAccessor _httpContextAccessor;

        /// <summary>
        /// 构造函数，注入 IHttpContextAccessor。
        /// </summary>
        /// <param name="httpContextAccessor">Http 上下文访问器</param>
        public AuditInterceptor(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        /// <summary>
        /// 同步拦截 SaveChanges 操作，在保存前处理审计字段。
        /// </summary>
        /// <param name="eventData">DbContext 事件数据，包含当前上下文信息</param>
        /// <param name="result">拦截结果对象</param>
        /// <returns>拦截结果对象</returns>
        public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
        {
            HandleAuditFields(eventData);
            return base.SavingChanges(eventData, result);
        }

        /// <summary>
        /// 异步拦截 SaveChangesAsync 操作，在保存前处理审计字段。
        /// </summary>
        /// <param name="eventData">DbContext 事件数据，包含当前上下文信息</param>
        /// <param name="result">拦截结果对象</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>拦截结果对象的异步任务</returns>
        public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
            DbContextEventData eventData,
            InterceptionResult<int> result,
            CancellationToken cancellationToken = default)
        {
            HandleAuditFields(eventData);
            return base.SavingChangesAsync(eventData, result, cancellationToken);
        }

        /// <summary>
        /// 统一处理所有被追踪实体的审计字段。
        /// 只处理继承自 BaseEntity 的实体。
        /// </summary>
        /// <param name="eventData">DbContext 事件数据</param>
        /// <exception cref="ArgumentNullException">如果 eventData.Context 为空则抛出</exception>
        private void HandleAuditFields(DbContextEventData eventData)
        {


            if (eventData?.Context == null)
                throw new ArgumentNullException(nameof(eventData.Context), "DbContext 不能为空");

            // 检测实体状态变更
            eventData.Context.ChangeTracker.DetectChanges();

            // 遍历所有被追踪的实体
            foreach (var entry in eventData.Context.ChangeTracker.Entries())
            {
                // 只处理继承自 BaseEntity 的实体
                if (entry.Entity is not BaseEntity auditable)
                    continue;

                switch (entry.State)
                {
                    case EntityState.Added:
                        // 新增时，设置创建人、创建时间等字段
                        SetAuditFields(auditable, AuditAction.Add);
                        break;
                    case EntityState.Modified:
                        // 修改时，设置修改人、修改时间等字段
                        SetAuditFields(auditable, AuditAction.Update);
                        break;
                    case EntityState.Deleted:
                        // 删除时，设置删除人、删除时间，并做软删除（IsDelete=true，状态改为 Modified）
                        SetAuditFields(auditable, AuditAction.Delete);
                        entry.State = EntityState.Modified; // 软删除，实际变为修改
                        break;
                    case EntityState.Unchanged:
                        // 未变更实体不处理
                        break;
                    case EntityState.Detached:
                        // 已分离实体不处理
                        break;
                    default:
                        throw new InvalidOperationException($"不支持的实体状态: {entry.State}");
                }
            }
        }

        /// <summary>
        /// 设置单个实体的审计字段（创建、修改、删除）。
        /// </summary>
        /// <param name="entity">需要设置审计字段的实体</param>
        /// <param name="action">审计操作类型（新增、修改、删除）</param>
        /// <exception cref="ArgumentOutOfRangeException">不支持的审计操作类型</exception>
        private void SetAuditFields(BaseEntity entity, AuditAction action)
        {
            var time = DateTime.Now;
            // 获取当前用户信息（如无则用系统默认）
            var claims = _httpContextAccessor.HttpContext?.User?.Claims;

            // 判断是否为用户注册场景（没有token的情况）
            bool isUserRegistration = claims == null || !claims.Any();

            // 如果是用户注册，使用默认管理员信息
            if (isUserRegistration && action == AuditAction.Add && entity is User)
            {
                // 设置默认管理员ID为1，名称为"系统管理员"
                entity.AddId = 2;  // 管理员ID
                entity.AddName = "admin";
                entity.AddTime = time;
                return;
            }
            //不是注册则正常进行
            int userId = Convert.ToInt32(claims?.FirstOrDefault(x => x.Type == "UserId")?.Value ?? "0");
            var userName = claims?.FirstOrDefault(x => x.Type == "UserName")?.Value ?? "系统";

            switch (action)
            {
                case AuditAction.Add:
                    // 新增时设置创建人、创建时间
                    entity.AddId = userId;
                    entity.AddName = userName;
                    entity.AddTime = time;
                    break;
                case AuditAction.Update:
                    // 修改时设置修改人、修改时间
                    entity.UpdateId = userId;
                    entity.UpdateName = userName;
                    entity.UpdateTime = time;
                    break;
                case AuditAction.Delete:
                    // 删除时设置删除人、删除时间，并做软删除
                    entity.DeleteId = userId;
                    entity.DeleteName = userName;
                    entity.DeleteTime = time;
                    entity.IsDelete = true;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(action), action, "不支持的审计操作类型");
            }
        }
    }
}
